package cn.jdemo.socket.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Reactor 模式也叫 Dispatcher 模式，即 I/O 多路复用监听事件，收到事件后，根据事件类型分配（Dispatch）给某个进程 / 线程。
 *
 * @see Selector#open() == epoll_create() epoll实例中有arrayWrap
 * @see SelectableChannel#register(Selector, int) == epoll_ctl() 注册监听事件
 * @see Selector#select() == epoll_wait() 阻塞等待,发生的事件
 *
 * @see ByteBuffer#allocate(int)
 * @see ByteBuffer#array()
 */
public class Demo02 {

    public static void main(String[] args) throws IOException {
        ServerSocketChannel sSocketChannel = ServerSocketChannel.open();
        sSocketChannel.configureBlocking(false);// 非阻塞
        sSocketChannel.bind(new InetSocketAddress(8001));

        // 1.打开一个 IO 监视器：Selector
        Selector selector = Selector.open();// == epoll_create

        // 2.将sSocketChannel注册到 Selector 上，并监听 OP_ACCEPT 事件
        sSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// == epoll_ctl

        // 3.业务处理
        handleSocket(selector);
    }

    public static void handleSocket(Selector selector) throws IOException {

        while (true){
            // 1.阻塞,等待事件发生
            int select = selector.select();// == epoll_wait

            // 2.获取事件集合
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                // 3.处理事件业务(优化:增加线程池处理事件)
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isAcceptable()) {
                    ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel socketChannel = channel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                }
                if (selectionKey.isReadable()){
                    SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
                    ByteBuffer byteBf = ByteBuffer.allocate(8);
                    int read = socketChannel.read(byteBf);
                    if (read != -1){
                        System.out.println(new String(byteBf.array(), 0,read));
                    }
                }
                // 4.从事件集合移除已处理事件
                iterator.remove();
            }
        }
    }
}